#include <stdio.h>
#if 0
#include "DisjointSet.h"
#else
#endif
#include <stdlib.h>
#include <memory.h>

/* Disjoint-set forest data structure. */
typedef struct DisjointSet
{
    int *parent;            /* index to parent node; 0 if root */
    unsigned char *rank;    /* depth of children before path compression */
    int num_items;          /* number of items */
    int num_disjoint;       /* number of disjoint subsets */
} DisjointSet;

/* Creates a disjoint set. */
void DisjointSet_create(DisjointSet *set, int num_items)
{
    set->num_items = num_items;
    set->num_disjoint = num_items;
    set->parent = calloc(num_items + 1, sizeof(set->parent[0]));
    set->rank = calloc(num_items + 1, sizeof(set->rank[0]));
}

/* Destroys a disjoint set. */
void DisjointSet_destroy(DisjointSet *set)
{
    free(set->parent);
    free(set->rank);
}

/* Finds the representative of a given element. Both the input index and 
 * the returned index are one-based. If 'item' = 0, returns 0.
 */
int DisjointSet_find(DisjointSet *set, int item)
{
    int parent = set->parent[item];
    if (item == 0)                  /* null item */
        return 0;
    if (parent == 0)                /* item is root */
        return item;
    if (set->parent[parent] == 0)   /* parent is root (path compressed) */
        return parent;
    return set->parent[item] = DisjointSet_find(set, parent);
}

/* Checks whether two items are in the same class. If one of x and y is 
 * zero and the other is non-zero, they are considered not connected. 
 * If both x and y are zero, they are considered not connected.
 */
int DisjointSet_connected(DisjointSet *set, int x, int y)
{
    if (x == 0 || y == 0)
        return 0;
    else
        return DisjointSet_find(set, x) == DisjointSet_find(set, y);
}

/* Merges two subsets. Returns the representative of the merged sets. 
 * If either is zero, returns the other. If both are zero, returns 0.
 */
int DisjointSet_merge(DisjointSet *set, int x, int y)
{
    x = DisjointSet_find(set, x);
    y = DisjointSet_find(set, y);
    if (x == y)
        return x;
    if (x == 0)
        return y;
    if (y == 0)
        return x;
    
    --set->num_disjoint;        /* update number of disjoint sets */

    if (set->rank[x] < set->rank[y])
    {
        set->parent[x] = y;     /* append x to y */
        return y;
    }
    else if (set->rank[x] > set->rank[y])
    {
        set->parent[y] = x;     /* append y to x */
        return x;
    }
    else
    {
        set->parent[x] = y;     /* append x to y (arbitrary) */
        set->rank[y]++;         /* increase rank */
        return y;
    }
}

#define MAX_ITEMS   50000

/* The relationships between classes are maintained as a collection of
 * doubly-linked lists. Each linked list has one, two or three elements:
 * one element at the beginning, two elements if X->Y is established, 
 * and three elements if X->Y->Z->X is established. Here X,Y,Z denote 
 * the representative of the respective classes.
 */
static unsigned short prec[MAX_ITEMS+1]; /* who eats me; 0 if no one */
static unsigned short succ[MAX_ITEMS+1]; /* i eat whom; 0 if no one */

int solve()
{
    int N, K, i, conflicts = 0;
    DisjointSet set;

    if (scanf("%d %d", &N, &K) != 2)
        return 0;
    
    memset(prec, 0, sizeof(prec[0])*(N+1)); /* 1-based */
    memset(succ, 0, sizeof(succ[0])*(N+1));
    DisjointSet_create(&set, N);

    for (i = 0; i < K; i++)
    {
        int d, x, y, r1, r2, r3;
        scanf("%d %d %d", &d, &x, &y);

        /* Sanity check. */
        if (x < 1 || x > N || y < 1 || y > N || (d == 2 && x == y))
        {
            ++conflicts;
            continue;
        }
        
        /* Find the representative of X and Y. */
        x = DisjointSet_find(&set, x);
        y = DisjointSet_find(&set, y);

        if (d == 1) /* assertion: X and Y are in the same class */
        {
            /* If X and Y are known to be in the same class, do nothing. */
            if (x == y)
                continue;

            /* If X is known to eat Y or vice versa, it's a conflict. */
            if (succ[x] == y || succ[y] == x)
            {
                ++conflicts;
                continue;
            }

            /* Merge (x, y), (prec[x], prec[y]), (succ[x], succ[y]). */
            r1 = DisjointSet_merge(&set, prec[x], prec[y]);
            r2 = DisjointSet_merge(&set, x, y);
            r3 = DisjointSet_merge(&set, succ[x], succ[y]);
            prec[r1] = r3; succ[r1] = r2;
            prec[r2] = r1; succ[r2] = r3;
            prec[r3] = r2; succ[r3] = r1;
        }
        else        /* assertion: X eats Y */
        {
            /* If X is known to eat Y, then do nothing. */
            if (succ[x] == y)
                continue;

            /* If X and Y are known to be in the same class or Y is known 
             * to eat X, then conflict. */
            if (x == y || succ[y] == x)
            {
                ++conflicts;
                continue;
            }

            /* Establish X->Y and merge the linked list of X and Y. */
            r1 = DisjointSet_merge(&set, prec[y], x);
            r2 = DisjointSet_merge(&set, succ[x], y);
            r3 = DisjointSet_merge(&set, succ[y], prec[x]);
            prec[r1] = r3; succ[r1] = r2;
            prec[r2] = r1; succ[r2] = r3;
            prec[r3] = r2; succ[r3] = r1;
        }
    }
    printf("%d\n", conflicts);
    DisjointSet_destroy(&set);
    return 1;
}

int main()
{
    while (solve());
    return 0;
}